package labox.innovation.gameserver.instancemanager;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javolution.io.UTF8StreamReader;
import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.xml.stream.XMLStreamConstants;
import javolution.xml.stream.XMLStreamException;
import javolution.xml.stream.XMLStreamReaderImpl;

import labox.innovation.config.Config;
import labox.innovation.db.core.L2DatabaseFactory;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.model.entity.Instance;

/**
 * 
 * ����������,������Ϸ�����е����и�������
 * 
 * @author evill33t, GodKratos
 * 
 */
public final class InstanceManager {

	private final static Logger _log = LoggerFactory.getLogger(InstanceManager.class.getName());

	private Map<Integer, Instance> _instanceList = new FastMap<Integer, Instance>();//��������,�������еĸ���

	private Map<Integer, InstanceWorld> _instanceWorlds = new FastMap<Integer, InstanceWorld>();

	private int _dynamic = 300000;

	// InstanceId Names
	private final static Map<Integer, String> _instanceIdNames = new FastMap<Integer, String>();

	//         objectID  instanceID, time
	private Map<Long, Map<Integer, Long>> _playerInstanceTimes = new FastMap<Long, Map<Integer, Long>>();

	private static final String ADD_INSTANCE_TIME = "INSERT INTO character_instance_time (charId,instanceId,time) values (?,?,?) ON DUPLICATE KEY UPDATE time=?";

	private static final String RESTORE_INSTANCE_TIMES = "SELECT instanceId,time FROM character_instance_time WHERE charId=?";

	private static final String DELETE_INSTANCE_TIME = "DELETE FROM character_instance_time WHERE charId=? AND instanceId=?";

	public class InstanceWorld {
		public int instanceId;

		public int templateId = -1;

		public FastList<Integer> allowed = new FastList<Integer>();

		public int status;
	}

	private InstanceManager() {
		_log.info("Initializing InstanceManager", Config.SERVER_ID, "����������");
		loadInstanceNames();
		_log.info("Loaded " + _instanceIdNames.size() + " instance names", Config.SERVER_ID, "��������");
		createWorld();
	}

	public static final InstanceManager getInstance() {
		return SingletonHolder._instance;
	}

	/**
	 * ͨ�����ID�͸���ID��ø�����ʱ��
	 * 
	 * @param playerObjId
	 * @param id
	 * @return
	 */
	public long getInstanceTime(long playerObjId, int id) {
		if (!_playerInstanceTimes.containsKey(playerObjId))
			restoreInstanceTimes(playerObjId);
		if (_playerInstanceTimes.get(playerObjId).containsKey(id))
			return _playerInstanceTimes.get(playerObjId).get(id);
		return -1;
	}

	/**
	 * ��ö��������и����е�ʱ��
	 * 
	 * @param playerObjId
	 * @return
	 */
	public Map<Integer, Long> getAllInstanceTimes(int playerObjId) {
		if (!_playerInstanceTimes.containsKey(playerObjId))
			restoreInstanceTimes(playerObjId);
		return _playerInstanceTimes.get(playerObjId);
	}

	/**
	 * ���ö�����ĳ��������ʱ��
	 * @param playerObjId
	 * @param id
	 * @param time
	 */
	public void setInstanceTime(long playerObjId, int id, long time) {
		if (!_playerInstanceTimes.containsKey(playerObjId))
			restoreInstanceTimes(playerObjId);
		_playerInstanceTimes.get(playerObjId).put(id, time);
	}

	/**
	 * ɾ��ʱ��
	 * 
	 * @param playerObjId
	 *            player�����ID
	 * @param id
	 *            ������ID
	 */
	public void deleteInstanceTime(long playerObjId, int id) {
		_playerInstanceTimes.get(playerObjId).remove(id);
	}

	/**
	 * �洢һ������ĸ���->ʱ�����
	 * @param playerObjId
	 */
	public void restoreInstanceTimes(long playerObjId) {
		if (_playerInstanceTimes.containsKey(playerObjId))
			return; // already restored
		_playerInstanceTimes.put(playerObjId, new FastMap<Integer, Long>());
	}

	/**
	 * ͨ��ID��ø��������
	 * @param id
	 * @return
	 */
	public String getInstanceIdName(int id) {
		if (_instanceIdNames.containsKey(id))
			return _instanceIdNames.get(id);
		return "UnknownInstance";
	}

	private void loadInstanceNames() {
		InputStream in = null;
		try {
			File f = new File(Config.DATAPACK_ROOT, "/data/instancenames.xml");
			if (f.exists()) {
				in = new FileInputStream(f);
				XMLStreamReaderImpl xpp = new XMLStreamReaderImpl();
				xpp.setInput(new UTF8StreamReader().setInput(in));
				for (int e = xpp.getEventType(); e != XMLStreamConstants.END_DOCUMENT; e = xpp.next()) {
					if (e == XMLStreamConstants.START_ELEMENT) {
						if (xpp.getLocalName().toString().equals("instance")) {
							Integer id = Integer.valueOf(xpp.getAttributeValue(null, "id").toString());
							String name = xpp.getAttributeValue(null, "name").toString();
							_instanceIdNames.put(id, name);//����ID��name
						}
					}
				}
			} else {
				_log.info("instancenames.xml could not be loaded: file not found", Config.SERVER_ID);
			}
		} catch (Exception e) {
			_log.error("instancenames.xml could not be loaded:error", Config.SERVER_ID, e);
		} finally {
			try {
				if (in != null)
					in.close();
			} catch (Exception e) {
			}
		}
	}

	public void addWorld(InstanceWorld world) {
		_instanceWorlds.put(world.instanceId, world);
	}

	public InstanceWorld getWorld(int instanceId) {
		return _instanceWorlds.get(instanceId);
	}

	public InstanceWorld getPlayerWorld(FuzePcInstance player) {
		for (InstanceWorld temp : _instanceWorlds.values()) {
			if (temp == null)
				continue;
			// check if the player have a World Instance where he/she is allowed to enter
			if (temp.allowed.contains(player.getObjectId()))
				return temp;
		}
		return null;
	}

	private void createWorld() {
		Instance themultiverse = new Instance(-1);//IDΪ-1�ĸ���Ϊ��Ԫ���������еĶ��󶼿��Կ���
		themultiverse.setName("multiverse");
		_instanceList.put(-1, themultiverse);
		_log.info("Multiverse Instance created", Config.SERVER_ID, "GMʹ�õĶ�Ԫ����");

		Instance universe = new Instance(0);//IDΪ0�ĸ���Ϊ�����Ϸ���磬�Ǹ����ĵĶ��󶼿��Կ���
		universe.setName("universe");
		_instanceList.put(0, universe);
		_log.info("Universe Instance created", Config.SERVER_ID, "��Ϸ����");
	}

	/**
	 * ͨ��ID�Ӹ����б��к͸���������ɾ���һ������
	 * 
	 * @param instanceid
	 */
	public void destroyInstance(int instanceid) {
		if (instanceid <= 0)
			return;
		Instance temp = _instanceList.get(instanceid);
		if (temp != null) {
			temp.removeNpcs();
			temp.removePlayers();
			temp.cancelTimer();
			_instanceList.remove(instanceid);
			if (_instanceWorlds.containsKey(instanceid))
				_instanceWorlds.remove(instanceid);
		}
	}

	/**
	 * ͨ��ID��ø�������
	 * 
	 * @param instanceid
	 * @return
	 */
	public Instance getInstance(int instanceid) {
		return _instanceList.get(instanceid);
	}

	/**
	 * ��ȡ�����б�
	 * 
	 * @return
	 */
	public Map<Integer, Instance> getInstances() {
		return _instanceList;
	}

	/**
	 * ���player���ڵĸ���ID
	 * 
	 * @param objectId
	 * @return
	 */
	public int getPlayerInstance(long objectId) {
		for (Instance temp : _instanceList.values()) {
			if (temp == null)
				continue;
			// check if the player is in any active instance
			if (temp.containsPlayer(objectId))
				return temp.getId();
		}
		// 0 is default instance aka the world
		return 0;
	}

	/**
	 * ͨ����ID�����������󣬲����뵽�����б���
	 * 
	 * @param id
	 *            ���iD
	 * @return
	 */
	public boolean createInstance(int id) {
		if (getInstance(id) != null)
			return false;

		Instance instance = new Instance(id);
		_instanceList.put(id, instance);
		return true;
	}

	/**
	 * ͨ����ID��ģ���ļ�������һ������
	 * 
	 * @param id
	 * @param template
	 * @return
	 * @throws FileNotFoundException
	 */
	public boolean createInstanceFromTemplate(int id, String template) throws FileNotFoundException {
		if (getInstance(id) != null)
			return false;
		Instance instance = new Instance(id);
		_instanceList.put(id, instance);
		instance.loadInstanceTemplate(template);
		return true;
	}

	/**
	 * 
	 * ͨ���ĸ���ģ���ļ����ڲ���̬��ID����300000��ʼ������һ������
	 * 
	 * @param template
	 *            xml file
	 * @return
	 */
	public int createDynamicInstance(String template) {
		while (getInstance(_dynamic) != null) {
			_dynamic++;
			if (_dynamic == Integer.MAX_VALUE) {
				_log.warn("InstanceManager: More then " + (Integer.MAX_VALUE - 300000) + " instances created", Config.SERVER_ID, null);
				_dynamic = 300000;
			}
		}
		if (template != null) {
			try {
				createInstanceFromTemplate(_dynamic, template);
			} catch (FileNotFoundException e) {
				_log.error("InstanceManager: Failed creating instance from template " + template + ", ", Config.SERVER_ID, e);
				e.printStackTrace();
			}
		}
		return _dynamic;
	}

	@SuppressWarnings("synthetic-access")
	private static class SingletonHolder {
		protected static final InstanceManager _instance = new InstanceManager();
	}
}
